package pub.cleangao.mybatis.dao;


import lombok.extern.slf4j.Slf4j;
import pub.cleangao.common.util.BeanUtil;
import pub.cleangao.common.view.Page;
import pub.cleangao.mybatis.runtime.QueryException;
import pub.cleangao.mybatis.spi.BeanExecutor;
import pub.cleangao.mybatis.spi.SpiContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 查询工具
 */
@Slf4j
public class QuerySupport {


    private QuerySupport() {
    }


    /**
     * 查询并转换结果集
     *
     * @param mapper mapper
     * @param query  查询条件
     * @param clazz  返回VO的类型
     * @param <E>    结果集
     * @param <T>    VO
     * @return 返回查询结果
     * @see #queryAndConvert(RetrieveMapper, Query, Supplier)
     */
    public static <E, T> Page<T> queryAndConvert(RetrieveMapper<E, ?> mapper, Query query, Class<T> clazz) {
        return queryAndConvert(mapper, query, () -> {
            try {
                return clazz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                log.error("new -> Object error, class=" + clazz, e);
                return null;
            }
        });
    }

    /**
     * 查询并转换结果集
     *
     * @param mapper   mapper
     * @param query    查询条件
     * @param supplier 返回实例对象
     * @param <E>      结果集
     * @param <T>      实例对象
     * @return 返回查询结果
     */
    public static <E, T> Page<T> queryAndConvert(RetrieveMapper<E, ?> mapper, Query query, Supplier<T> supplier) {
        Page<E> pageInfo = query(mapper, query);
        List<E> list = pageInfo.getData();
        Page<T> rst = new Page<>();
        BeanUtil.copyPropertiesIgnoreNull(pageInfo, rst);
        if (list != null) {
            List<T> newList = new ArrayList<>(list.size());
            BeanExecutor beanExecutor = SpiContext.getBeanExecutor();
            for (Object element : list) {
                T t = supplier.get();
                beanExecutor.copyProperties(element, t);
                newList.add(t);
            }
            rst.setData(newList);
        } else {
            rst.setData(Collections.<T>emptyList());
        }
        return rst;
    }


    /**
     * 分页数算法:页数 = (总记录数 + 每页记录数 - 1) / 每页记录数
     *
     * @param total    总记录数
     * @param pageSize 每页记录数
     * @return 返回页数
     */
    public static int calcPageCount(long total, int pageSize) {
        return (int) (pageSize == 0 ? 1 : (total + pageSize - 1) / pageSize);
    }


    /**
     * 分页查询
     *
     * @param <Entity> 实体类
     * @param mapper   查询mapper
     * @param query    查询条件
     * @return 返回PageInfo，里面存放结果以及分页信息
     */
    public static <Entity> Page<Entity> query(RetrieveMapper<Entity, ?> mapper, Query query) {
        return query(mapper, query, Page.class);
    }


    /**
     * 分页查询
     *
     * @param <Entity>        实体类
     * @param <T>             返回结果类
     * @param mapper          查询mapper
     * @param query           查询条件
     * @param pageResultClass 结果类
     * @return 返回结果类
     */
    public static <Entity, T extends Page<Entity>> T query(RetrieveMapper<Entity, ?> mapper, Query query, Class<T> pageResultClass) {
        T result = null;
        try {
            result = pageResultClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new QueryException(e);
        }

        try {
            // 总页数
            int pageCount = 0;
            // 总条数
            long total = 0L;
            // 结果集
            List<Entity> list = Collections.emptyList();

            // 如果是查询全部则直接返回结果集条数
            // 如果是分页查询则还需要带入条件执行一下sql
            if (query.isQueryAll()) {
                list = mapper.list(query);
                total = list.size();
                if (total > 0) {
                    pageCount = 1;
                }
            } else {
                if (query.isTotalSet()) {
                    //如果设置了total总记录数，直接获取该total
                    total = query.getTotal();
                } else {
                    //如果没有设置total，先去count执行一下sql
                    total = mapper.getCount(query);
                    query.setTotal(total);
                }
                // 如果有数据
                if (total > 0) {
                    list = mapper.list(query);
                    mergeQueryPageInfo(query, result);
                    pageCount = calcPageCount(total, query.getPageSize());
                }
            }
            result.setData(list);
            result.setCount(total);
            result.setMaxPageNo(pageCount);
        } catch (Exception e) {
            throw new QueryException(e);
        }
        return result;
    }


    /**
     * 分页查询,指定列名
     *
     * @param columns 数据库列名
     * @param mapper  查询mapper
     * @param query   查询条件
     * @return 返回PageInfo
     */
    public static <T> Page<T> query(List<String> columns, RetrieveMapper<?, ?> mapper, Query query, Class<T> clazz) {
        if (columns == null || columns.isEmpty()) {
            throw new IllegalArgumentException("parameter 'columns' can not empty");
        }
        Objects.requireNonNull(query, "parameter 'query' can not null");
        Objects.requireNonNull(clazz, "parameter 'clazz' can not null");
        Page<T> result = new Page<>();
        try {
            // 总页数
            int pageCount = 0;
            // 总条数
            long total = 0;
            // 结果集
            List<T> list = Collections.emptyList();

            // 如果是查询全部则直接返回结果集条数
            // 如果是分页查询则还需要带入条件执行一下sql
            if (query.isQueryAll()) {
                list = mapper.listSpecificColumns(columns, query, clazz);
                total = list.size();
                if (total > 0) {
                    pageCount = 1;
                }
            } else {
                if (query.isTotalSet()) {
                    //如果设置了total总记录数，直接获取该total
                    total = query.getTotal();
                } else {
                    //如果没有设置total，先去count执行一下sql
                    total = mapper.getCount(query);
                }
                // 如果有数据
                if (total > 0) {
                    list = mapper.listSpecificColumns(columns, query, clazz);
                    mergeQueryPageInfo(query, result);
                    pageCount = calcPageCount(total, query.getPageSize());
                }
            }
            result.setData(list);
            result.setCount(total);
            result.setMaxPageNo(pageCount);
        } catch (Exception e) {
            throw new QueryException(e);
        }
        return result;
    }


    private static <T> void mergeQueryPageInfo(Query query, Page<T> result) {
        long offset = query.getOffset();
        // 每页记录数
        int pageSize = query.getPageSize();
        // 当前第几页
        long pageIndex = (offset / pageSize) + 1;
        result.setOffset(offset);
        result.setPageNo((int) pageIndex);
        result.setPageSize(pageSize);
    }

    /**
     * 自定义分页查询
     * <p>
     * PageInfo<UserInfoDTO> pageInfo = MapperUtil.query(query, mapper::getUserInfoTotal, mapper::getUserInfoList);
     * </p>
     *
     * @param query       查询条件
     * @param totalGetter 获取总数
     * @param listGetter  获取结果集
     * @param <T>         结果集类型
     * @return 返回分页信息
     */
    public static <T> Page<T> query(Query query, Function<Query, Long> totalGetter, Function<Query, List<T>> listGetter) {
        return query(query, totalGetter, listGetter, Page::new);
    }

    /**
     * 自定义分页查询
     *
     * @param query       查询条件
     * @param totalGetter 获取总数
     * @param listGetter  获取结果集
     * @param <T>         结果集类型
     * @param <R>         分页结果类型
     * @return 返回分页信息
     */
    public static <T, R extends Page<T>> R query(Query query, Function<Query, Long> totalGetter, Function<Query, List<T>> listGetter, Supplier<R> pageInfoGetter) {
        R result = pageInfoGetter.get();
        try {
            // 总页数
            int pageCount = 0;
            // 总条数
            long total = 0L;
            // 结果集
            List<T> list = Collections.emptyList();
            // 如果是查询全部则直接返回结果集条数
            // 如果是分页查询则还需要带入条件执行一下sql
            if (query.isQueryAll()) {
                list = listGetter.apply(query);
                total = list.size();
                if (total > 0) {
                    pageCount = 1;
                }
            } else {
                if (query.isTotalSet()) {
                    //如果设置了total总记录数，直接获取该total
                    total = query.getTotal();
                } else {
                    //如果没有设置total，先去count执行一下sql
                    total = totalGetter.apply(query);
                }
                // 如果有数据
                if (total > 0) {
                    list = listGetter.apply(query);
                    long offset = query.getOffset();
                    // 每页记录数
                    int pageSize = query.getPageSize();
                    // 当前第几页
                    long pageIndex = (offset / pageSize) + 1;
                    result.setPageNo((int) pageIndex);
                    result.setPageSize(pageSize);
                    pageCount = QuerySupport.calcPageCount(total, pageSize);
                }
            }
            result.setData(list);
            result.setCount(total);
            result.setMaxPageNo(pageCount);
        } catch (Exception e) {
            throw new QueryException(e);
        }
        return result;
    }

}
